Mobile development: Netstat, know your device’s open ports

On desktop PCs you have the nice tool netstat to see which ports are open on the PC. A customer wanted to know, why his devices do not release there internet connection. The only tool I know, that will show open network connections, is called netstat. Unfortunately I did not find such tool for Windows Mobile and so I wrote one myself:

netstat for windows mobile

netstatCF2

NetstatCF2 is written in C#, Compact Framework. After you started the tool it immediately collects the open ports data. It will also log this data periodically (every 3 seconds) to a log file called “\netstat.log”.

The code makes massive calls to the ipHlp API functions. Here are some sample snippets:

...
    public class IPHlpAPI32Wrapper
    {
        public const byte NO_ERROR = 0;
        public const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
        public const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
        public const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
        public int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS;

        [DllImport("iphlpapi.dll", SetLastError = true)]
        public extern static int GetUdpStatistics(ref MIB_UDPSTATS pStats);

        [DllImport("iphlpapi.dll", SetLastError = true)]
        public static extern int GetUdpTable(byte[] UcpTable, out int pdwSize, bool bOrder);

        [DllImport("iphlpapi.dll", SetLastError = true)]
        public extern static int GetTcpStatistics(ref MIB_TCPSTATS pStats);

        [DllImport("iphlpapi.dll", SetLastError = true)]
        public static extern int GetTcpTable(byte[] pTcpTable, out int pdwSize, bool bOrder);

        [DllImport("iphlpapi.dll", SetLastError = true)]
        public static extern int GetIpForwardTable(IntPtr pIpForwardTable, ref int pdwSize, bool bOrder);
        [DllImport("iphlpapi.dll", SetLastError = true)]
        public static extern int GetIpForwardTable(byte[] pIpForwardTable, ref int pdwSize, bool bOrder);
...

        public void GetTcpConnexions()
        {
            byte[] buffer = new byte[20000]; // Start with 20.000 bytes left for information about tcp table
            int pdwSize = 20000;
            int res = IPHlpAPI32Wrapper.GetTcpTable(buffer, out pdwSize, true);
            if (res != NO_ERROR)
            {
                buffer = new byte[pdwSize];
                res = IPHlpAPI32Wrapper.GetTcpTable(buffer, out pdwSize, true);
                if (res != 0)
                    return;     // Error. You should handle it
            }

            TcpConnexion = new IpHlpApidotnet.MIB_TCPTABLE();

            int nOffset = 0;
            // number of entry in the
            TcpConnexion.dwNumEntries = Convert.ToInt32(buffer[nOffset]);
            nOffset += 4;
            TcpConnexion.table = new MIB_TCPROW[TcpConnexion.dwNumEntries];

            for (int i = 0; i < TcpConnexion.dwNumEntries; i++)
            {
                // state
                int st = Convert.ToInt32(buffer[nOffset]);
                // state in string
                //((MIB_TCPROW)
                (TcpConnexion.table[i]).StrgState = convert_state(st);
                // state  by ID
                //((MIB_TCPROW)
                (TcpConnexion.table[i]).iState = st;
                nOffset += 4;
                // local address
                string LocalAdrr = buffer[nOffset].ToString() + "." + buffer[nOffset + 1].ToString() + "." + buffer[nOffset + 2].ToString() + "." + buffer[nOffset + 3].ToString();
                nOffset += 4;
                //local port in decimal
                int LocalPort = (((int)buffer[nOffset]) << 8) + (((int)buffer[nOffset + 1])) +
                    (((int)buffer[nOffset + 2]) << 24) + (((int)buffer[nOffset + 3]) << 16);

                nOffset += 4;
                // store the remote endpoint
                //((MIB_TCPROW)(
                (TcpConnexion.table[i]).Local = new IPEndPoint(IPAddress.Parse(LocalAdrr), LocalPort);

                // remote address
                string RemoteAdrr = buffer[nOffset].ToString() + "." + buffer[nOffset + 1].ToString() + "." + buffer[nOffset + 2].ToString() + "." + buffer[nOffset + 3].ToString();
                nOffset += 4;
                // if the remote address = 0 (0.0.0.0) the remote port is always 0
                // else get the remote port in decimal
                int RemotePort;
                //
                if (RemoteAdrr == "0.0.0.0")
                {
                    RemotePort = 0;
                }
                else
                {
                    RemotePort = (((int)buffer[nOffset]) << 8) + (((int)buffer[nOffset + 1])) +
                        (((int)buffer[nOffset + 2]) << 24) + (((int)buffer[nOffset + 3]) << 16);
                }
                nOffset += 4;
                //((MIB_TCPROW)
                (TcpConnexion.table[i]).Remote = new IPEndPoint(IPAddress.Parse(RemoteAdrr), RemotePort);
            }
        }
...
        public void GetUdpConnexions()
        {
            byte[] buffer = new byte[20000]; // Start with 20.000 bytes left for information about tcp table
            int pdwSize = 20000;
            int res = IPHlpAPI32Wrapper.GetUdpTable(buffer, out pdwSize, true);
            if (res != NO_ERROR)
            {
                buffer = new byte[pdwSize];
                res = IPHlpAPI32Wrapper.GetUdpTable(buffer, out pdwSize, true);
                if (res != 0)
                    return;     // Error. You should handle it
            }

            UdpConnexion = new IpHlpApidotnet.MIB_UDPTABLE();

            int nOffset = 0;
            // number of entry in the
            UdpConnexion.dwNumEntries = Convert.ToInt32(buffer[nOffset]);
            nOffset += 4;
            UdpConnexion.table = new MIB_UDPROW[UdpConnexion.dwNumEntries];
            for (int i = 0; i < UdpConnexion.dwNumEntries; i++)
            {
                string LocalAdrr = buffer[nOffset].ToString() + "." + buffer[nOffset + 1].ToString() + "." + buffer[nOffset + 2].ToString() + "." + buffer[nOffset + 3].ToString();
                nOffset += 4;

                int LocalPort = (((int)buffer[nOffset]) << 8) + (((int)buffer[nOffset + 1])) +
                    (((int)buffer[nOffset + 2]) << 24) + (((int)buffer[nOffset + 3]) << 16);
                nOffset += 4;
                //((MIB_UDPROW)
                (UdpConnexion.table[i]).Local = new IPEndPoint(IPAddress.Parse(LocalAdrr), LocalPort);
            }
        }
...
    public class IPHelper
    {
        private const int NO_ERROR = 0;
        private const int MIB_TCP_STATE_CLOSED = 1;
        private const int MIB_TCP_STATE_LISTEN = 2;
        private const int MIB_TCP_STATE_SYN_SENT = 3;
        private const int MIB_TCP_STATE_SYN_RCVD = 4;
        private const int MIB_TCP_STATE_ESTAB = 5;
        private const int MIB_TCP_STATE_FIN_WAIT1 = 6;
        private const int MIB_TCP_STATE_FIN_WAIT2 = 7;
        private const int MIB_TCP_STATE_CLOSE_WAIT = 8;
        private const int MIB_TCP_STATE_CLOSING = 9;
        private const int MIB_TCP_STATE_LAST_ACK = 10;
        private const int MIB_TCP_STATE_TIME_WAIT = 11;
        private const int MIB_TCP_STATE_DELETE_TCB = 12;

        private const int ERROR_INSUFFICIENT_BUFFER = 122;
...

But there is nothing special except on how to use C/C++ structures in C#.

Here is a sample of the logged data:

xxxxxxxxxxx AM #################
======= TCP table ========
local                       remote
        0.0.0.0:    21         0.0.0.0:     0 LISTEN
        0.0.0.0:  1004         0.0.0.0:     0 LISTEN
        0.0.0.0:  2188         0.0.0.0:     0 LISTEN
        0.0.0.0:  2189         0.0.0.0:     0 LISTEN
        0.0.0.0:  5655         0.0.0.0:     0 LISTEN
        0.0.0.0: 52241         0.0.0.0:     0 LISTEN
      127.0.0.1:  1032       127.0.0.1: 52241 ESTAB
      127.0.0.1:  1034       127.0.0.1: 52241 ESTAB
      127.0.0.1:  1035       127.0.0.1: 52241 ESTAB
      127.0.0.1:  1036       127.0.0.1: 52241 ESTAB
      127.0.0.1: 52241       127.0.0.1:  1032 ESTAB
      127.0.0.1: 52241       127.0.0.1:  1034 ESTAB
      127.0.0.1: 52241       127.0.0.1:  1035 ESTAB
      127.0.0.1: 52241       127.0.0.1:  1036 ESTAB
 192.168.55.101:  1082  192.168.55.100:  7438 ESTAB
 192.168.55.101:  1083  192.168.55.100:   990 ESTAB
 192.168.55.101:  1086  192.168.55.100:   990 ESTAB
 192.168.55.101:  1087  192.168.55.100:   990 ESTAB
 192.168.55.101:  1092  192.168.55.100:   990 ESTAB
 192.168.55.101:  1102  192.168.55.100:  1004 ESTAB
 192.168.55.101:  1103  192.168.55.100:   990 ESTAB
192.168.128.104:  1033   192.168.128.5: 62241 ESTAB
192.168.128.104:  5655   192.168.128.2: 59534 ESTAB
192.168.128.104:  5655   192.168.128.2: 59535 ESTAB
192.168.128.104:  6510   192.168.128.2: 59536 ESTAB
192.168.128.104:  6510   192.168.128.2: 59537 ESTAB
======= UDP table ========
        0.0.0.0:    53
        0.0.0.0:   137
        0.0.0.0:   138
        0.0.0.0:  1088
        0.0.0.0:  9204
        0.0.0.0: 49111
192.168.128.104:    68
======= TCP statistics ========
Retransmission timeout (min/max): Van Jacobson's Algorithm: 300/120000
                max connnections: -1
                     active open: 69
                    passive open: 196
                 failed attempts: 0
              established resets: 243
             current established: 20
                     segments in: 134380
                    segments out: 130900
          retransmitted segments: 175
                       in errors: 0
                      out resets: 861
                 num connections: 26
======= UDP statistics ========
                    in datagrams: 13771
                       in errors: 0
                       num ports: 3353
                   num addresses: 7
                   out datagrams: 887
======= Adapter infos ==========
131074: BCMCF1, 192.168.128.104
262147: USB Cable:, 192.168.55.101
======= Route entries ==========
Network Destination        Netmask          Gateway          Interface     Metric
0.0.0.0                    0.0.0.0          192.168.55.101                   003    0
225.20.0.0                 0.0.0.0          255.255.255.255                  255    0
0.0.0.0                    0.0.0.0          192.168.128.1                    002    0
225.20.0.0                 0.0.0.0          255.255.255.255                  255    0
127.0.0.0                  255.0.0.0        127.0.0.1                        001    0
104.148.1.0                0.0.0.0          255.255.255.255                  255    0
192.168.55.101             255.255.255.255  127.0.0.1                        001    0
225.20.0.0                 0.0.0.0          255.255.255.255                  255    0
192.168.55.255             255.255.255.255  192.168.55.101                   003    0
225.20.0.0                 0.0.0.0          255.255.255.255                  255    0
192.168.128.0              255.255.255.0    192.168.128.104                  002    0
67.148.1.0                 0.0.0.0          255.255.255.255                  255    0

Hopefully you use this small tool at a special time.

Code and sample bin at code.google.com (VS2008, CF2, WM5SDK)

Possibly outdated bin: [Download not found]